package mlp;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.PrintStream;

import javax.swing.JOptionPane;

import digitRecognitionProblem.DigitRecognitionMlpFitness;

/**
 * Generates neural networks, all with same number of neurons in hidden layer
 * and initial weights range
 */
public class MlpGenUniformStructure {

	private static int trainSize  = 38000;
	
	// indices for main arguments
	private enum ArgNums {
		TEST_FILE, TRAIN_FILE, START_DIGIT, END_DIGIT, LEARNING_RATE, MOMENTUM, ERROR_RATE, TRAIN_SIZE, FROM_INDEX, MLP_AMOUNT, HIDDEN_LAYER, INIT_WEIGHTS
	}
	
	public static void main(String[] args) {
		try {
			File file = new File("Details.txt");
			FileOutputStream fos;
			fos = new FileOutputStream(file);
			PrintStream ps = new PrintStream(fos);
			System.setOut(ps);
			System.setErr(ps);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		try {
			
			String testFileName = args[ArgNums.TEST_FILE.ordinal()];
			String trainFileName = args[ArgNums.TRAIN_FILE.ordinal()];
			int startDigit = Integer.parseInt(args[ArgNums.START_DIGIT.ordinal()]);
			int endDigit = Integer.parseInt(args[ArgNums.END_DIGIT.ordinal()]);
			Mlp.LEARNING_RATE = Float.parseFloat(args[ArgNums.LEARNING_RATE.ordinal()]);
			Mlp.MOMENTUM = Float.parseFloat(args[ArgNums.MOMENTUM.ordinal()]);
			Mlp.ERROR_RATE = Float.parseFloat(args[ArgNums.ERROR_RATE.ordinal()]);
			trainSize = Integer.parseInt(args[ArgNums.TRAIN_SIZE.ordinal()]);
			int fromIndex = Integer.parseInt(args[ArgNums.FROM_INDEX.ordinal()]); 
			int mlpAmount = Integer.parseInt(args[ArgNums.MLP_AMOUNT.ordinal()]); 
			int hiddenLayer = Integer.parseInt(args[ArgNums.HIDDEN_LAYER.ordinal()]);
			Mlp.INIT_WEIGHT_RANGE = Float.parseFloat(args[ArgNums.INIT_WEIGHTS.ordinal()]);
			
			Mlp.NUMBER_EXAMPLE = trainSize;
			
			// document arguments
			System.out.println("Test file = " + testFileName);
			System.out.println("Train file = " + trainFileName);
			System.out.println("Starts from digit = " + startDigit);
			System.out.println("Ends at digit = " + endDigit);
			System.out.println("Learning rate = " + Mlp.LEARNING_RATE);
			System.out.println("Momentum = " + Mlp.MOMENTUM);
			System.out.println("Error rate = " + Mlp.ERROR_RATE);
			System.out.println("Train size = " + trainSize);
			System.out.println("From index = " + fromIndex);
			System.out.println("Mlp amount = " + mlpAmount);
			System.out.println("Hidden layer = " + hiddenLayer);
			System.out.println("Initial weights range: " + "[-" + Mlp.INIT_WEIGHT_RANGE + "," + Mlp.INIT_WEIGHT_RANGE + "]");
			System.out.println("******************************************");

			// train network for each digit
			for (int digit = startDigit ; digit <= endDigit ; ++digit) {

				DigitRecognitionMlpFitness.initTestData(testFileName, digit);
				
				Mlp.HIDDEN_LAYYER = hiddenLayer;				
								
				// create directory for networks learning current digit
				String path = "" + digit;

				File dir = new File(path);
				dir.mkdir();
				
				for(int i = fromIndex ; i < fromIndex+mlpAmount ; ++i)
				{
					
					DigitRecognitionMlpFitness.initTestData(testFileName, digit);
					System.out.println("************* MLP-"+digit+"_"+i+" *************");
					MlpExamples trainExamples = new MlpExamples(trainFileName, i*trainSize, i*trainSize+trainSize, digit);
					int nn_neurons[] = {	
							hiddenLayer, // layer 1: first hidden layer
							Mlp.OUTPUT_LAYYER // layer 2: output layer
					};

					Mlp mlp = Mlp.createsByLayersSize(nn_neurons);
					mlp.setThreshold(0.9f);
					mlp.learn(trainExamples.getInput(), trainExamples.getOutput());
					DigitRecognitionMlpFitness funcFit = new DigitRecognitionMlpFitness(mlp);
							
					System.out.println("MLP"+i+ "(DIGIT "+digit+") :  Success rate on test set: "+ funcFit.getFitness(null));				
					System.out.println("************************************");
					FileOutputStream writeFile;
					try {
						writeFile = new FileOutputStream(path + "/" + "mlp_" + i + "_rec_" +digit +".txt");
						ObjectOutputStream fileStream = new ObjectOutputStream(writeFile);
						fileStream.writeObject(mlp);
						fileStream.close();
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}					
				}
			}			
			
			JOptionPane.showMessageDialog(null, "Building the networks is finished");						
		} 
		catch (Exception e) {
			System.out.println("***********************ERROR*********************************");
			System.out.println(e.getMessage()+"\n");
			
			e.printStackTrace();
			
			System.out.println("**************************************************************");
			JOptionPane.showMessageDialog(null, "ERROR, see Details.txt", "Error", JOptionPane.ERROR_MESSAGE);
		}
	}
}
